package cn.dansj.controller;

import cn.dansj.common.request.annotation.http.GetMethod;
import cn.dansj.common.request.annotation.http.PostMethod;
import cn.dansj.common.request.annotation.http.RequestMappingMethod;
import cn.dansj.common.request.annotation.http.RequestParam;
import cn.dansj.common.utils.bean.Return;
import cn.dansj.entity.User;
import cn.dansj.common.utils.enums.BooleanType;
import cn.dansj.enums.GlobalEnv;
import cn.dansj.service.RoleMenuService;
import cn.dansj.service.UserService;
import cn.dansj.common.request.RequestUtils;
import cn.dansj.common.utils.transfer.Transformation;
import cn.dansj.common.utils.transfer.RandomUtils;
import cn.dansj.common.utils.transfer.Verification;
import cn.dansj.configure.EncryptAndDecrypt;
import cn.dansj.socket.WebSocketServer;
import cn.dansj.utils.project.ProjectUtils;
import cn.dansj.common.redis.RedisUtils;
import com.alibaba.fastjson2.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMethod;

import java.security.NoSuchAlgorithmException;
import java.util.Map;

@RequestMappingMethod
public class LoginController {
    private final RedisUtils redisUtils;
    private final UserService userService;
    private final RoleMenuService roleMenuService;
    private final EncryptAndDecrypt encryptAndDecrypt;

    public LoginController(RedisUtils redisUtils, UserService userService, RoleMenuService roleMenuService, EncryptAndDecrypt encryptAndDecrypt) {
        this.redisUtils = redisUtils;
        this.userService = userService;
        this.roleMenuService = roleMenuService;
        this.encryptAndDecrypt = encryptAndDecrypt;
    }

    @RequestMappingMethod(value = "/login", method = RequestMethod.OPTIONS, permission = BooleanType.TRUE)
    public Return<?> checkLogin(@RequestParam String username, @RequestParam(decrypt = true) String password, @RequestParam String platform) {
        Return<User> userReturn = ProjectUtils.checkLoginfo(username, password);
        if (userReturn.getStatus() && "console".equalsIgnoreCase(platform) && (!GlobalEnv.SuperAdmin.equals(userReturn.getData().getRole()) && roleMenuService.getAllByRole(userReturn.getData().getRole()).isEmpty())) {
            return Return.T(false, "权限不足, 禁止登录");
        }
        return userReturn.setData(null);
    }

    @PostMethod(value = "/login", permission = BooleanType.TRUE)
    public Return<?> login(@RequestParam String username, @RequestParam(decrypt = true) String password, @RequestParam String platform) {
        Return<User> userReturn = ProjectUtils.checkLoginfo(username, password);
        if (userReturn.getStatus() && "console".equalsIgnoreCase(platform) && (!GlobalEnv.SuperAdmin.equals(userReturn.getData().getRole()) && roleMenuService.getAllByRole(userReturn.getData().getRole()).isEmpty())) {
            return Return.T(false, "权限不足, 禁止登录");
        }
        RequestUtils.getRequest().setAttribute("platform", platform);
        //验证通过
        if (userReturn.getStatus()) {
            return ProjectUtils.initUserLoginData(userReturn.getData());
        } else {
            return userReturn;
        }
    }

    @GetMethod(value = "/loginfo/getData", permission = BooleanType.TRUE, encrypt = BooleanType.TRUE)
    public Return<?> loginfo() throws NoSuchAlgorithmException {
        JSONObject loginData = ProjectUtils.getSettingByRedis("login");
        loginData.put("publicKey", encryptAndDecrypt.getKeyPem().publicKey());
        return Return.T(loginData);
    }

    @PostMethod(value = "/logout", permission = BooleanType.TRUE)
    public Return<?> logout(@RequestParam String token) {
        redisUtils.delete(GlobalEnv.CacheTokenPrefix + token);
        return Return.T(true, "remove token successful");
    }

    /**
     * 获取二维码token
     */
    @GetMethod(value = "/getLoginQrCode", permission = BooleanType.TRUE)
    public Return<?> qrCodeToken(@RequestParam(decrypt = true) String source) {
        String sequence = RandomUtils.getUid();
        int qrCodeExpire = ProjectUtils.getSettingByRedis("login", "qr.code.expire.second", 60);
        final Map<String, String> mapData = Map.of("source", Transformation.nvl(source, "qrCodeLogin"), "userAgent", RequestUtils.getRequest().getHeader("user-agent"));
        boolean status = redisUtils.hmset(GlobalEnv.QRCodePrefix + sequence, mapData, qrCodeExpire);
        return Return.T(status ? sequence : "").setStatus(true);
    }

    /**
     * 扫码后第一次判断，根据sequence获取数据库缓存的二维码序列号，判断二维码状态及权限
     */
    @GetMethod(value = "/getLoginQrCodeToken")
    public Return<?> getLoginQrCodeToken(@RequestParam("sequence") String sequence) {
        if (ProjectUtils.checkMobileUserAgent()) {
            String source = Transformation.toString(redisUtils.hget(GlobalEnv.QRCodePrefix + sequence, "source"));
            final boolean tokenNotNull = Verification.checkNotNull(source);
            return Return.T(tokenNotNull, tokenNotNull ? ProjectUtils.encryptByPrivateKey(source) : "");
        } else {
            return Return.T(false, "非法请求");
        }
    }

    /**
     * 确认身份接口：确定身份以及判断是否二维码过期等
     * 第三次判断，返回token信息
     */
    @GetMethod(value = "/bindUserIdAndToken")
    public Return<?> bindUserIdAndToken(@RequestParam("sequence") String sequence, @RequestParam("userSequence") String userSequence, HttpServletRequest request) {
        try {
            String source = Transformation.toString(redisUtils.hget(GlobalEnv.QRCodePrefix + sequence, "source"));
            Return<?> returnData;
            if (Verification.checkNotNull(source)) {
                request.setAttribute("platform", source);
                request.setAttribute("checkMobile", "0");// login from qrCode
                returnData = ProjectUtils.initUserLoginData(userService.getUserBySequence(userSequence));
            } else {
                returnData = Return.T(false, "二维码已过期");
            }
            WebSocketServer.sendInfo(returnData.toJSONString(), sequence);
            return Return.T(true, "扫码请求处理完毕");
        } catch (Exception e) {
            WebSocketServer.sendInfo(Return.T(false, "扫码失败,请联系管理员").toJSONString(), sequence);
            return Return.T(false, "扫码请求处理失败");
        }
    }

    /**
     * 锁定，取消 二维码状态
     * 第二次判断
     * -1 锁定二维码
     * 0 取消扫码
     */
    @GetMethod(value = "/lockQrToken", permission = BooleanType.TRUE)
    public Return<?> lockQrToken(@RequestParam("sequence") String sequence, @RequestParam("status") String status) {
        try {
            boolean hasKey = redisUtils.hasKey(GlobalEnv.QRCodePrefix + sequence);
            Return<?> returnData = Return.T(hasKey, status);
            WebSocketServer.sendInfo(returnData.toJSONString(), sequence);
            return Return.T(true, "请求处理完毕");
        } catch (Exception e) {
            return Return.T(false, "请求处理失败");
        }
    }
}
